# -*- coding:utf-8 -*-

"""
=============================
this is a feibot product,
all rights reserved.
www.feibot.com
@author: Gu Tianyuan
@email: g@feibot.com
@time: ${DATE}:${TIME}
@product: ${PRODUCT_NAME}
=============================
"""

import hashlib
import os
import random
import signal
import sys
import time
import traceback
import urllib
import urllib.error
import urllib.parse
import urllib.request
from urllib.error import URLError

import urllib
from gtyConfig import configFileHandler, systemConfig
from gtyTools import gtyLog, gtyTypes, tools
from . import OssHandler
import shutil
import glob

machineConfig = configFileHandler.UserConfigHandler()

serverLocation = machineConfig.read("server", "serverLocation", "string", "China")
if serverLocation == "China":
    serverLocation = os.environ.get("platform4_serverLocation")
if serverLocation == "Customize 1":
    serverLocation = "https://feibot.tropevent.com/time/api/"
if serverLocation[-1] != '/':
    serverLocation += "/"

serverLocation2 = machineConfig.read("server2", "serverLocation", "string", "")
if serverLocation2[-1] != '/':
    serverLocation2 += "/"

cpDataUploadUrl = machineConfig.read("server", "cpDataUploadUrl")
dataFileDir = machineConfig.read("IO", "dataFileDir")
if dataFileDir[-1] != '/':
    dataFileDir += '/'

# user = os.environ.get("platform4_tagFormatUser")
hardwareType = machineConfig.read('machine', 'hardwareType')


# 获取赛事配置文件的下载地址
def obtainUrlOfEventConfigFile(machineId):
    t = urllib.request.urlopen(
        serverLocation + machineConfig.read("server", "eventConfigFileUrl") + '/' + machineId)  # 新老兼容 老 “?machineId=”
    return t.read()


# 从指定的地址下载文件到指定目录
def downloadFileFromWeb(fromUrl, toFile="/home/feibot/plateform/config/filename.txt"):
    try:
        import urllib.request, urllib.error, urllib.parse
        if fromUrl == '':
            fromUrl = serverLocation + machineConfig.read("server", "eventConfigFileUrl")
        f = urllib.request.urlopen(fromUrl)
        data = f.read()
        with open(toFile, "wb") as code:
            code.write(data)
            code.flush()
        return 'success'
    except   Exception as e:
        gtyLog.log.write(__file__, e)
        print(e)
        return 'failed'


# 记录EPC信息到文件，默认格式
# 输入应该为
# fileName:文件名
# tagList: 成员为标签对象，
def recordEpcDefaultFormat(fileUrl, tagList, reformat='', recordFileFormat=':'):
    if tagList is None or len(tagList) == 0:
        return False
    # 首先打开文件
    try:
        f = open(fileUrl, 'a')
    except Exception as e:
        os.system("touch " + fileUrl)
        gtyLog.log.write(__file__, e)
        return False
    # space
    space = '_'
    if reformat == 'blank space':
        space = ' '
    elif reformat == 'T':
        space = "T"
    # 对于每一条EPC值记录一行
    for tag in tagList:
        if hardwareType == 'A400':
            line = tag.epcString + recordFileFormat + tag.hardwareDateString + space + tag.hardwareTimeString \
                   + ",port=" + str(tag.channelId) \
                   + ",rssi=" + str(tag.rssi) \
                   + ",bat=" + str(tag.bat)
        else:
            line = tag.epcString + recordFileFormat + tag.hardwareDateString + space + tag.hardwareTimeString \
                   + ",port=" + str(tag.channelId) \
                   + ",rssi=" + str(tag.rssi)
        try:
            f.write(line)
            f.write('\n')
        except Exception as e:
            gtyLog.log.write(__file__, 'record epc into file error', e)
    f.flush()
    f.close()


# 记录EPC信息到文件，默认格式
# 输入应该为
# fileName:文件名
# tagList: 成员为标签对象，
def recordEpcISOFormat(fileUrl, tagList, timeZone=''):
    if tagList is None or len(tagList) == 0:
        return False
    # 首先打开文件
    try:
        f = open(fileUrl, 'a')
    except Exception as e:
        os.system("touch " + fileUrl)
        gtyLog.log.write(__file__, e)
        return False
    # 对于每一条EPC值记录一行
    for tag in tagList:
        line = tag.epcString \
               + "," + tag.hardwareDateString + "T" + tag.hardwareTimeString + timeZone \
               + ",port=" + str(tag.channelId) \
               + ",rssi=" + str(tag.rssi)
        try:
            f.write(line)
            f.write('\n')
        except Exception as e:
            gtyLog.log.write(__file__, 'record epc into file error', e)
    f.flush()
    f.close()


def recordEpcChris(fileUrl, tagList):
    if tagList is None or len(tagList) == 0:
        return False
    try:
        f = open(fileUrl, 'a')
    except Exception as e:
        os.system("touch " + fileUrl)
        gtyLog.log.write(__file__, e)
        return False
    dateTimeReformat = ' '
    for tag in tagList:
        # # 去除毫秒
        # shortTime = tag.hardwareTimeString.split('.')[0]
        timestamp = f"{tag.hardwareDateString}{dateTimeReformat}{tag.hardwareTimeString}"
        line = tag.epcString + "," + timestamp + "," + str(tag.channelId) + ",-" + str(tag.rssi) + "\n"
        try:
            f.write(line)
        except Exception as e:
            gtyLog.log.write(__file__, 'record epc into file error', e)
    f.flush()
    f.close()


# 秘鲁客户要求记录到txt中
def recordEpcTXTKoji(fileUrl, tagList):
    configHandlers = gtyTypes.ConfigHandlers()
    machineId = tools.getMachineId(configHandlers.mmc.read("machine", "machineId"))
    if tagList is None or len(tagList) == 0:
        return False
    # 首先打开文件
    try:
        f = open(fileUrl, 'a')
    except Exception as e:
        os.system("touch " + fileUrl)
        gtyLog.log.write(__file__, e)
        return False
    # 对于每一条EPC值记录一行
    for tag in tagList:
        line = tag.epcString[-16:] + ";" + machineId + ";" + str(tag.channelId) + ";" + tag.hardwareTimeString + "0"
        try:
            f.write(line)
            f.write('\n')
        except Exception as e:
            gtyLog.log.write(__file__, 'record epc into txt file error', e)
    f.flush()
    f.close()


# 美国客户按照指定格式保存到txt中
def recordEpcTxtAaron(fileUrl, tagList):
    if tagList is None or len(tagList) == 0:
        return False  # 首先打开文件
    try:
        f = open(fileUrl, 'a')
    except Exception as e:
        os.system("touch " + fileUrl)
        gtyLog.log.write(__file__, e)
        return False
    # 对于每一条EPC值记录一行
    for tag in tagList:
        line = str(tag.channelId) + ',' \
               + tag.epcString + ',' \
               + tag.epcString[-4:] + ',' \
               + '"' + tag.hardwareDateString + ' ' + tag.hardwareTimeString + '"'
        try:
            f.write(line)
            f.write('\n')
        except Exception as e:
            gtyLog.log.write(__file__, 'record epc into txt file error', e)
    f.flush()
    f.close()


# 按行写入数据到文件
def recordLines(fileUrl, lineList):
    if lineList is None or len(lineList) == 0:
        return False
    # 首先打开文件
    try:
        os.system("rm " + fileUrl)
        os.system("touch " + fileUrl)
        f = open(fileUrl, 'a')
    except Exception as e:
        gtyLog.log.write(__file__, e)
        return False
    # 对于每一条EPC值记录一行
    for line in lineList:
        try:
            f.write(line)
            f.write('\n')
        except Exception as e:
            gtyLog.log.write(__file__, 'record section data into file error', e)
    f.flush()
    f.close()
    return True


def jsonToFile(data, path):
    configHandlers = gtyTypes.ConfigHandlers()
    machineId = tools.getMachineId(configHandlers.mmc.read("machine", "machineId"))

    event_keys = ['eventName', 'eventId']  # 前两个参数属于 [event]
    machine_keys = ['timingPoint', 'machinePosition', 'prio', 'minTime', 'outputFileName', 'readerPower',
                    'tagCheckFileName', 'tagCheckFontSize', 'tagCheckFontColor',
                    'softwareVersion', 'firmwareVersion', 'linuxDaemonVersion', 'machine_configcol',
                    'readerStart']  # 之后的参数属于 [test]
    # 初始化分组字典
    event_dict = {}
    machine_dict = {}
    # 遍历字典并根据键的前缀分组
    for key, value in data.items():
        if key in event_keys:
            event_dict[key] = value
        elif key in machine_keys:
            machine_dict[key] = value
        else:
            # 如果键不属于任何分组，可以单独处理或忽略
            print(f"Key '{key}' does not belong to any group.")
    try:
        with open(path, 'w') as file:
            # 写入 [event] 部分
            file.write("[event]\n")
            for key, value in event_dict.items():
                file.write(f"{key} = {value}\n")  # 直接写入键值对

            # 写入 [machineId] 部分
            file.write("\n[" + machineId + "]\n")
            for key, value in machine_dict.items():
                file.write(f"{key} = {value}\n")  # 直接写入键值对

        print("配置文件已保存到 :", path)
        return 'success'
    except Exception as e:
        gtyLog.log.write(__file__, 'download config from new server error', e)
        print(e)
        return 'failed'


# 从服务器下载配置文件
def downloadFileFromServer(resultQ, eventId, machineId,IO_eventQ):
    if 'time-data-cn' in serverLocation:
        import json
        # 对于新计时后台此处直接传回json数据，对json数据进行处理
        # url就为json数据,处理后保存到一个新的文件
        url = obtainUrlOfEventConfigFile(machineId).decode(encoding='utf-8')
        dictJson = json.loads(url)
        # print('=======dictJson:', dictJson)
        if dictJson['code'] == 'ok':
            data = dictJson['data']
            eventIdJson = str(data['eventId'])
            fileName = 'ec_' + eventIdJson + '.ecg'
            path = '/home/feibot/platform/configFiles/' + fileName
            print(fileName, path)
            result = jsonToFile(data, path)
    else:
        # 1. 获取配置文件的下载地址
        url = obtainUrlOfEventConfigFile(machineId).decode(encoding='utf-8')
        # 对于设备没有在项目中的情况
        if 'ec_.ecg' in url:
            # 事件：下载赛事配置文件的结果，下载失败==> 发送到赛事配置窗口
            res = ['ui_downloadEventConfigFileFromServerResult', 'failed, not in event']
            resultQ.put(res)
        # 2. 下载配置文件
        fileName = url.split('/')[-1]
        result = downloadFileFromWeb(url, '/home/feibot/platform/configFiles/' + fileName)
        gtyLog.log.write(__file__, "downloadFileFromServer: ", result, fileName)
    if result == 'success':
        try:
            stateConfig = configFileHandler.ConfigFileHandler(systemConfig.runStateFilePath)
            stateConfig.write('machine', 'eventConfigFileName', fileName)
            IO_eventQ.put(['io_downloadEventConfigFileSuccess'])
        except Exception as e:
            print(e)
        # 事件：下载赛事配置文件的结果，下载成功。==> 发送到赛事配置窗口
        res = ['ui_downloadEventConfigFileFromServerResult', str(fileName)]
        resultQ.put(res)
        # 事件：刷新赛事配置窗口的显示。==> 发送到赛事配置窗口
        res = ['ui_updateCurrentEventConfigFileName', str(fileName)]
        resultQ.put(res)

        # 事件：刷新赛事信息的显示。==>发送到主界面窗口
        res = ['ui_updateMainDialogEventInfo', str(fileName)]
        resultQ.put(res)
    else:
        # 事件：下载赛事配置文件的结果，下载失败==> 发送到赛事配置窗口
        res = ['ui_downloadEventConfigFileFromServer', 'failed']
        resultQ.put(res)
    return result


# 测试是否能连上互联网
def pingTest():
    t = '114.215.44.98'
    cmd = 'ping -c 1 -w 1 %s' % t
    backInfo = os.system(cmd + "> /dev/null 2>&1")
    if backInfo == 0:
        return True
    return False


# 检测是否能连上服务器
def testWebConnection(eventQ, machineId, batteryPercent, epcTotal, epcDiffent,
                      reader1Working,
                      reader2Working, machineTimeStamp, eventId='unKnown',
                      softwareBranch='none', softwareCommitId='none', firmwareVersion='none'):
    # 0. 输出自己的pid
    gtyLog.log.write(__file__, 'testWebConnection', os.getpid())
    try:
        try:
            md5String = os.environ.get("platform4_md5String")
            if md5String is None:
                md5String = ""
        except Exception as e:
            md5String = ""
            gtyLog.log.write(__file__, e)
        tocken = str(random.randint(10000000, 99999999))
        url = serverLocation + machineConfig.read("server", "internetConnectionTest") + \
              '?machineId=' + machineId + \
              '&batPercent=' + str(batteryPercent) + \
              '&epcTotal=' + str(epcTotal) + \
              '&epcDiff=' + str(epcDiffent) + \
              '&eventId=' + str(eventId) + \
              '&t=' + str(tocken) + \
              '&sign=' + str(get_token(tocken, md5String)) + \
              '&reader1Working=' + str(reader1Working) + \
              '&reader2Working=' + str(reader2Working) + \
              '&machineTimeStamp=' + str(machineTimeStamp) + \
              '&softwareBranch=' + str(softwareBranch) + \
              '&softwareCommitId=' + str(softwareCommitId) + \
              '&firmwareVersion=' + str(firmwareVersion)
        gtyLog.log.write(__file__, url)
        # print(__file__,url)
        t = urllib.request.urlopen(url, timeout=5)
        getData = t.read().decode(encoding='utf-8')
        gtyLog.log.write(__file__, getData)
        if "OK" in str(getData).upper():  # 待添加异常
            connection = 'connected'
            # print('connection ok!')
            # 读卡器的网络控制
            if 'reader1=0' in getData:
                res = ['ui_startOrStropReading',
                       ['1', 'offFromWeb']]  # 这里的参数是一个数组，['1',cmd]，这里cmd=on，开启，cmd=off，关闭，x不指定
                eventQ["UI"].put(res)

            if 'reader1=1' in getData:
                res = ['ui_startOrStropReading', ['1', 'onFromWeb']]  # 这里的参数是一个数组，['1',cmd]，这里cmd=on，开启，cmd=off，关闭，x不指定
                eventQ["UI"].put(res)

            if 'reader2=0' in getData:
                res = ['ui_startOrStropReading',
                       ['2', 'offFromWeb']]  # 这里的参数是一个数组，['1',cmd]，这里cmd=on，开启，cmd=off，关闭，x不指定
                eventQ["UI"].put(res)

            if 'reader2=1' in getData:
                res = ['ui_startOrStropReading', ['2', 'onFromWeb']]  # 这里的参数是一个数组，['1',cmd]，这里cmd=on，开启，cmd=off，关闭，x不指定
                eventQ["UI"].put(res)

            # 从服务器获取时间，并写入硬件。只有时间相差大于5秒时才写入
            if 'set_time=' in getData:
                index = getData.find('set_time=')
                if index != -1:
                    try:
                        res = ['ui_setDateTimeFromWeb', [getData[index + 9:index + 28]]]
                        eventQ["UI"].put(res)
                    except Exception as e:
                        print(e)

        else:
            if pingTest():
                connection = 'wwwConnected'
            else:
                connection = 'disconnected'
    except Exception as e:
        traceback.extract_stack()
        gtyLog.log.write(__file__, e)
        if pingTest():
            connection = 'wwwConnected'
        else:
            connection = 'disconnected'
    res = ['ui_internetConnectionReport', connection]
    eventQ["UI"].put(res)
    res = ['io_internetConnectionReport', connection]
    eventQ["IO"].put(res)


def testWebConnection2(eventQ, machineId, batteryPercent, epcTotal, epcDiffent,
                       reader1Working,
                       reader2Working, machineTimeStamp, eventId='unKnown'):
    url = ""
    getData = ""
    try:
        url = serverLocation2 + machineConfig.read("server2", "internetConnectionTest") + \
              '?machineId=' + machineId + \
              '&batPercent=' + str(batteryPercent) + \
              '&epcTotal=' + str(epcTotal) + \
              '&epcDiff=' + str(epcDiffent) + \
              '&eventId=' + str(eventId) + \
              '&reader1Working=' + str(reader1Working) + \
              '&reader2Working=' + str(reader2Working) + \
              '&machineTimeStamp=' + str(machineTimeStamp)
        t = urllib.request.urlopen(url, timeout=5)
        getData = t.read().decode(encoding='utf-8')
        gtyLog.log.write(__file__, "server2", url, getData)
    except Exception as e:
        traceback.extract_stack()
        gtyLog.log.write(__file__, e)
        print(__file__, "testConnection2Error:", url, getData)


# 上传结果文件到服务器并向UI线程返回结果信息
# 如果all为true，则上传全部文件
def uploadResultFileToServer(eventQ, recordFileObj, allFiles=False):
    recordFile = recordFileObj
    try:
        gtyLog.log.write(__file__, 'uploadResultFileToServer', os.getpid())

        # 首先判断服务器端是否允许上传
        eventId = recordFileObj.eventId
        t = urllib.request.urlopen(
            serverLocation + machineConfig.read("server", "dataUploadAllowedUrl") + '?eventId=' + eventId, timeout=5)
        getData = t.read().decode('utf-8')
        if 'no' in getData:
            res = ['ui_uploadResultFileToServer', ['failed', 'upload is not allowed by server']]
            eventQ["UI"].put(res)
            return

        # 如果允许，则上传
        # 获取待上传的文件列表
        fileUrlListOld = recordFile.getFileUrlListRecordDone(allFiles)
        fileUrlNow = recordFile.recordFileUrl
        fileUrlTotal = fileUrlListOld + [fileUrlNow]
        totalUrlNum = len(fileUrlTotal)
        successNum = 0
        for fileUrl in fileUrlTotal:
            if uploadFileToServer(fileUrl, recordFileObj.ossObjectPrefix):
                successNum += 1
                if fileUrl != fileUrlNow:
                    print(__file__, fileUrl)
                    eventQ["IO"].put(['io_oldFileUploadSuccessfully', fileUrl])

        if successNum > 0:
            res = ['ui_uploadResultFileToServer', ['successful', str(successNum) + "/" + str(totalUrlNum)]]
        else:
            res = ['ui_uploadResultFileToServer', ['failed', "all file upload failed"]]
        eventQ["UI"].put(res)
    except Exception as e:
        gtyLog.log.write(__file__, '上传原始文件进程超时，被杀死1', os.getpid(), e)
    exit()


# 上传日志文件到oss
def uploadLogFilesToOss(eventQ, logFileList):
    totalLogFileNum = len(logFileList)
    successNum = 0
    configHandlers = gtyTypes.ConfigHandlers()
    ossLogFilePrefix = configHandlers.machine.read("oss", "ossLogFilePrefix", "string", "")
    machineId = tools.getMachineId(configHandlers.mmc.read("machine", "machineId"))
    try:
        for logFile in logFileList:
            if uploadFileToServer(logFile, ossLogFilePrefix + machineId + "_"):
                successNum += 1
                print(__file__, 'upload log file to oss successfully', logFile)
                cmd = "rm " + os.path.join(os.path.dirname(logFile), "*.zip")
                os.popen(cmd)
                res = ['ui_uploadLogFileToServer', ['successful', str(successNum) + "/" + str(totalLogFileNum)]]
                eventQ["UI"].put(res)
        if successNum > 0:
            res = ['ui_uploadLogFileToServer', ['successful', str(successNum) + "/" + str(totalLogFileNum)]]
        else:
            res = ['ui_uploadLogFileToServer', ['failed', "all file upload failed"]]
        eventQ["UI"].put(res)
    except Exception as e:
        gtyLog.log.write(__file__, '上传原始文件进程超时，被杀死1', os.getpid(), e)
    exit()


# 上传一个指定文件到oss
def uploadFileToServer(filePath, objectPrefix=""):
    try:
        # 1.上传之前先压缩
        try:
            os.popen('rm ' + filePath + '.zip')
        except:
            pass
        cmd = 'zip -qj ' + filePath + '.zip ' + filePath
        os.system(cmd)
        filePath += '.zip'
        # 2.通过oss上传文件
        try:
            oss = OssHandler.OssHandler()
            targetFileName = objectPrefix + os.path.basename(filePath)
            if oss.uploadFileToOss(filePath, targetFileName):
                print('oss upload successfully:', targetFileName)
                return True
            else:
                print('oss upload failed:', time.strftime('%H:%M:%S', time.localtime(time.time())))
        except Exception as e:
            gtyLog.log.write(__file__, e)
    except Exception as e:
        gtyLog.log.write(__file__, e, '上传原始文件进程超时，被杀死2')
    # 删除所有zip文件
    try:
        cmd = "rm " + os.path.join(os.path.dirname(filePath), "*.zip")
        os.popen(cmd)
    except:
        pass
    return False


# 生成md5字串
def get_token(stra, secret=''):
    md5str = str(stra) + secret
    m1 = hashlib.md5()
    m1.update(md5str.encode("utf-8"))
    token = m1.hexdigest()
    return token


# 超时异常
def timeOutHandler(signum, frame):
    print('time out, pid:', os.getpid())
    raise Exception("timeout...")


# 查找并挂载u盘
# 这个函数必须在主线程中运行
def chooseUDiskFromList(parent, ulist):
    from PyQt5.QtWidgets import QInputDialog
    item, ok = QInputDialog.getItem(parent, "choose u-disk", "find u-disk:", ulist, 0, False)
    if ok and item:
        return item
    return 'error: user canceled u-disk selection.'


def getUDiskPath(parent=None):
    try:
        u = os.listdir('/media/feibot')  # 可能包含残留目录
        # 只保留真正挂载了的路径名
        u = [d for d in u if os.path.ismount(f"/media/feibot/{d}")]

        if len(u) == 0:
            return 'error: no usb disk found. Please check if it is connected and mounted.'
        if len(u) == 1:
            return u[0]
        return chooseUDiskFromList(parent, u)
    except Exception as e:
        print(e)
        return 'error: scan usb disk failed'


# 计算文件夹的总大小（包括子目录和文件）
def get_folder_size(folder_path):
    total_size = 0
    for dirpath, dirnames, filenames in os.walk(folder_path):
        for filename in filenames:
            file_path = os.path.join(dirpath, filename)
            total_size += os.path.getsize(file_path)
    return total_size


# 将文件复制到U盘的函数
# 传入参数：u盘名称，文件夹路径，进度队列，结果队列
def copyFileToU_fuc(uName,fileName, result_queue, progress_queue):
    if 'error' in uName:
        result_queue.put(uName)
        return

    # 使用 glob 解析通配符路径
    src_paths = glob.glob(f'/home/feibot/data/{fileName}')  # 使用通配符查找文件或目录
    if not src_paths:
        result_queue.put(f'error: source path {fileName} does not exist or match any files')
        return
    dst_path = f'/media/feibot/{uName}/'

    try:
        total_size = 0
        copied_size = 0
        # 计算总大小
        for src_path in src_paths:
            if os.path.isdir(src_path):
                total_size += get_folder_size(src_path)
            else:
                total_size += os.path.getsize(src_path)

        # 逐个文件复制并更新进度
        for src_path in src_paths:
            if os.path.isdir(src_path):
                # 遍历目录中的文件并逐一复制
                for root, dirs, files in os.walk(src_path):
                    for file in files:
                        src_file = os.path.join(root, file)
                        dst_file = os.path.join(dst_path, os.path.basename(src_path),
                                                os.path.relpath(src_file, src_path))
                        # 在u盘上创建目录
                        os.makedirs(os.path.dirname(dst_file), exist_ok=True)
                        # 复制文件
                        shutil.copy(src_file, dst_file)
                        # 更新已复制的大小
                        copied_size += os.path.getsize(dst_file)
                        progress_queue.put((copied_size, total_size))  # 发送进度信息
        result_queue.put('success: export file to usb disk successfully')
    except Exception as e:
        result_queue.put(f'error: {str(e)}')


if __name__ == '__main__':
    pass
